Android — 序列化的两种方式

前言

在看一些面试题的时候,竟然把序列化划分为基础知识。真是没接触就不知道它有多简单!本篇就来揭开序列化的神秘面纱,目录如下:

  1. 序列化是什么?
  2. 为什么要进行序列化?
  3. Android中的两种序列化方式概况
  4. Serializable 和 Parcelable 的区别
  5. Serializable 的使用
  6. Parcelable 的使用
  7. 更多参考

国际惯例,先放张图,QwQ

序列化是什么?

序列化是指把 Java 对象转化为字节序列并存储到一个存储媒介的过程。反之,把字节序列恢复到 Java 对象的过程则称为反序列化。

为什么要序列化?

Java 对象存在的一个前提是 JVM 有在运行。因此,如果 JVM 没有运行或者在其他机器的 JVM 上是不可能获取到指定 Java 对象的,而序列化操作则是把 Java 对象信息保存到存储媒介,可以在以上不可能的情况下仍然可以使用 Java 对象。

所以,序列化的主要作用是:

  • 永久保存对象,保存对象的字节序列到本地文件中
  • 通过序列化对象在网络中传递对象
  • 通过序列化在进程间传递对象

Android中的两种序列化方式概况

在Android开发中,经常需要再多个部件(Activity、Fragement)之间通过 Intent 传递一些数据,如果是一些普通类型的数据可以通过putExtra()进行传递,如果是对象的话就要先进行序列化才能传递了。在Android中有两种序列化的接口,Serializable 和 Parcelable。

Serializable 和 Parcelable 的区别

Serializable 是 Java的序列化接口。特点是简单,直接实现该接口就行了,其他工作都被系统完成了,但是对于内存开销大,序列化和反序列化需要很多的 I/O 流操作。

Parcelable 是Android的序列化方式,主要用于在内存序列化上,采用该方法需要实现该接口并且手动实现序列化过程,相对复杂点。如果序列化到存储设备(文件等)或者网络传输,比较复杂,建议用Serializable 方式。

最大的区别就是存储媒介的不同:Serializable 使用IO读写存储在硬盘上,Parcelable 是直接在内存中读写,内存读写速度远大于IO读写,所以Parcelable 更加高效。Serializable在序列化过程中会产生大量的临时变量,会引起频繁的GC,效率低下。

区别 Serializable Parcelable
所属API Java API Android SDK API
原理 反射,需要大量的I/O操作 不需要大量的I/O操作
开销
效率 很高
使用场景 序列化到本地或者通过网络传输 内存序列化

Serializable 的使用

使用很简单,只需要实现 Serializable 接口即可。例如:

1
2
3
4
5
6
7
8
9
10
11
public class StudentBean implements Serializable {
private String name;
//添加 transient 关键字不会被序列化
private transient int age;
public StudentBean(String name, int age) {
this.name = name;
this.age = age;
}
// 省去 get/set 方法
}

序列化到本地和反序列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class TestSer {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//序列化
// writeBean();
//反序列化
StudentBean bean = readBean();
System.out.println(bean.toString());
}
private static StudentBean readBean() throws IOException, ClassNotFoundException {
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(new File("D://addons/bean.txt")));
StudentBean bean = (StudentBean) inputStream.readObject();
return bean;
}
private static void writeBean() {
StudentBean bean = new StudentBean("Omooo", 21);
try {
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(new File("D://addons/bean.txt")));
outputStream.writeObject(bean);
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

即通过 ObjectOutputStream/ObjectInputStream 实现序列化和反序列。

在Android的运用:

即通过 Intent 传递序列化后对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void send(View view) {
StudentBean user = new StudentBean();
user.setName("Omooo");
user.setAge(20);
Intent intent = new Intent(this, SecondActivity.class);
//传输的第一种方法:直接调用putExtra()方法
intent.putExtra("user", user);
/**
//传输的第二种方法:Intent利用putExtras(注意s)传入bundle
Bundle bundle = new Bundle();
bundle.putSerializable("user", user);
intent.putExtras(bundle);
*/
startActivity(intent);
}

在跳转的Activity:

1
2
3
Intent intent = getIntent();
StudentBean user = (StudentBean) intent.getSerializableExtra("user");
mView.setText(user.getName() + " " + user.getAge());

注意:

  1. 序列化前、后的对象内容完全一致,但不是同一个对象
  2. 静态成员变量属于类,不会参与序列化过程
  3. 用 transient 关键字修饰的成员变量不参与序列化过程
  4. 可重写 writeObject()、readObject()来改变系统默认的序列化过程
  5. 可使用此类的子类 Externalizable 对序列化进行自定义控制

Parcelable 的使用

  1. 继承 Parcelable 接口
  2. 重写 describeContents()、writeToParcel()方法(Implement methods)
  3. 添加 Parcel形参的构造函数,公有静态常量CREATOR(Add Parcelable Implementation)

Android Studio 插件:Android Parcelable code generator

写完实体类,Generate界面(Alt + Insert键)选择Parcelable一键生成。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class User implements Parcelable {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public User() {
}
//省去 get/set 方法
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
dest.writeInt(this.age);
}
protected User(Parcel in) {
this.name = in.readString();
this.age = in.readInt();
}
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel source) {
return new User(source);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
}

在传递序列化后对象的时候和用Serializable一样,唯一不同的就是取对象的时候是:

1
2
3
Intent intent = getIntent();
User user = intent.getParcelableExtra("user");
mView.setText(user.getName() + "\n" + user.getAge());

也就是 getSerializableExtra / getParcelableExtra 的区别。

更多参考

https://www.jianshu.com/p/74a70fae99fb

https://www.jianshu.com/p/ed01c4b7a14d

https://www.jianshu.com/p/287acb9e396f

我们一直都向往,面朝大海,春暖花开。 但是几人能做到,心中有爱,四季不败?